#include "SpaceFortress.h"

#include "FinanceManager.h"

#include <assert.h>

FinanceManager::FinanceManager()
{
	// Initialize the production data.
	_data.size = 0;
	_data.capacity = 0;
	_data.buffer = NULL;

	_data.entity = NULL;
	_data.funds = NULL;
}

FinanceManager::~FinanceManager()
{
	// De-initialize the production data.
	if (_data.capacity) {
		delete[] _data.buffer;
	}
	_data.buffer = NULL;

	_data.entity = NULL;
	_data.funds = NULL;
}

void FinanceManager::Allocate(const uint32 capacity)
{
	assert(capacity > _production_data.capacity);

	InstanceData new_data;
	const uint32 bytes = capacity * (sizeof(Entity) + sizeof(float) + 2 * sizeof(int32));
	new_data.buffer = new unsigned char[bytes];
	new_data.size = _data.size;
	new_data.capacity = capacity;

	new_data.entity = (Entity*)(new_data.buffer);				// The entity owning this instance.
	new_data.funds = (int32*)(new_data.entity + capacity);

	memcpy(new_data.entity, _data.entity, _data.size * sizeof(Entity));
	memcpy(new_data.funds, _data.funds, _data.size * sizeof(int32));

	delete[] _data.buffer;
	_data = new_data;
}

void FinanceManager::SortData(const uint32 start, const uint32 stop)
{
	int32 i = start, j = stop;
	uint32 pivot = _data.entity[(start + stop) / 2].index();

	while (i <= j) {
		while (_data.entity[i].index() < pivot) {
			i++;
		}
		while (_data.entity[j].index() > pivot) {
			j--;
		}
		if (i <= j) {
			Entity temp_entity = _data.entity[i];
			int32 temp_funds = _data.funds[i];

			_data.entity[i] = _data.entity[j];
			_data.funds[i] = _data.funds[j];

			_data.entity[j] = temp_entity;
			_data.funds[j] = temp_funds;

			i++;
			j--;
		}
	}

	if (start < j) {
		SortData(start, j);
	}
	if (i < stop) {
		SortData(i, stop);
	}
}

// These functions should seperate out the successful and usuccessful orders because failed orders cannot be processed simultaneously while successful ones can.
void FinanceManager::RegisterBuyResults(const BidData* const bid_data, const uint32 num_resources)
{
	for (uint32 resource = 0; resource < num_resources; resource++) {
		const BidData& bd = bid_data[resource];

		uint32 i = 0, j = 0;
		while (i < _data.size && j < bd.size) {
			if (_data.entity[i].index() < bd.entity[j].index()) {
				i++;
			}
			else if (_data.entity[i].index() > bd.entity[j].index()) {
				j++;
			}
			else { // equal
				_data.funds[i] -= bd.clearing_price[j];
			}
		}
	}
}

void FinanceManager::RegisterSellResults(const BidData* const bid_data, const uint32 num_resources)
{
	for (uint32 resource = 0; resource < num_resources; resource++) {
		const BidData& bd = bid_data[resource];

		uint32 i = 0, j = 0;
		while (i < _data.size && j < bd.size) {
			if (_data.entity[i].index() < bd.entity[j].index()) {
				i++;
			}
			else if (_data.entity[i].index() > bd.entity[j].index()) {
				j++;
			}
			else { // equal
				_data.funds[i] += bd.clearing_price[j];
			}
		}
	}
}

void FinanceManager::FindBankruptEntities(OpenVector<Entity>& defunct_entities)
{
	uint32 shift_count = 0;

	for (uint32 i = 1; i < _data.size; i++) {
		// check to see if this entity is also in the list.
		if (_data.funds[i] > 0.f) {
			// Shift the data to the previous cell. 
			// Note that logically we must atleast start at index 1 (not 0) and if we are already at the end then the for loop quites before 1 iteration.
			_data.entity[i - shift_count] = _data.entity[i];
			_data.funds[i - shift_count] = _data.funds[i];
		}
		else {
			defunct_entities.Add(_data.entity[i]);

			// If it is increase the shift count and move on to the next index.
			shift_count++;
		}
	}

	_data.size -= defunct_entities.size;
}

void FinanceManager::AllocateFinance(const Entity* const entities, const uint32 num_entities)
{
	if (_data.size + num_entities > _data.capacity) {
		Allocate(_data.size + num_entities);
	}

	for (uint32 i = 0; i < num_entities; i++) {
		const uint32 idx = _data.size++;
		_data.entity[idx] = entities[i];
		_data.funds[idx] = 100; // Arbitrary value.
	}

	// Need to sort by entity.
	SortData(0, _data.size);
}